﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using SmlEngine.Graphics;
using SmlEngine.Extensions;

namespace SmlEngine.Sprites.Base.WorldMap
{
    /// <summary>
    /// Encapsulated a group of Texture2Ds loaded from arbitary spritesheets with support for animation.
    /// </summary>
    public sealed class WorldTile
    {
        private bool isContentLoaded;
        private bool isDataSet;
        public bool isRunning { get; private set; } // can be used to delay an animation
        public bool isRunningOnce { get; set; }

        private string graphicsSource;
        private List<Rectangle> graphicsSourceRects = new List<Rectangle>();

        private List<Texture2D> textures = new List<Texture2D>();

        public bool isAnimated { get; private set; }
        private int frameIndex;
        private int frameLength;
        private int framesDrawn;

        private Vector2 position;

        private float transparencyMultiplier = 1.0f;
        public float TransparencyMultiplier
        {
            get { return transparencyMultiplier; }
            set { transparencyMultiplier = MathHelper.Clamp(value, 0.0f, 1.0f); }
        }

        public Vector2 Size
        {
            get
            {
                if (graphicsSourceRects.Any())
                {
                    return new Vector2(graphicsSourceRects[0].Width, graphicsSourceRects[0].Height);
                }
                else return new Vector2(float.NaN, float.NaN);
            }
        }

        public Rectangle Area
        {
            get
            {
                if (textures != null && textures.Any())
                {
                    return new Rectangle(textures[0].Width, textures[0].Height, (int)position.X, (int)position.Y);
                }
                else return new Rectangle(Int32.MinValue, Int32.MinValue, Int32.MinValue, Int32.MinValue);
            }
        }

        public bool HasMultipleFrames
        {
            get
            {
                if (textures.Any())
                {
                    return textures.Count > 1;
                }
                else
                {
                    return graphicsSourceRects.Count > 1;
                }
            }
        }

        public bool HasAnimationEnded
        {
            get;
            private set;
            //get
            //{
            //    return isRunningOnce && frameIndex == textures.Count - 1 && !isRunning;
            //}
        }

        public WorldTile(string source, List<Rectangle> rects)
        {
            graphicsSource = source;
            graphicsSourceRects = rects;
        }

        public WorldTile(string source, List<Rectangle> rects, float transparency)
            : this(source, rects)
        {
            TransparencyMultiplier = transparency;
        }

        public WorldTile(GraphicsDevice gd, ContentManager cm, string source)
        {
            graphicsSource = source;
            //LoadContent(gd, cm, false);
            graphicsSourceRects = Vector2.Zero.ToRectangle(Size).ToRectList();
        }

        private WorldTile()
        {

        }

        public void LoadContent(GraphicsDevice gd, ContentManager cm, bool is_animated)
        {
            if (!isContentLoaded && !string.IsNullOrEmpty(graphicsSource) && graphicsSourceRects != null)
            {
                if (GraphicsHelper.IsContentPath(graphicsSource))
                {
                    foreach (Rectangle r in graphicsSourceRects)
                    {
                        textures.Add(SpritesheetManager.GetTile(graphicsSource, r, cm));
                    }
                }
                else
                {
                    Texture2D spritesheet = null;
                    spritesheet = spritesheet.LoadFromFile(graphicsSource, ref gd);

                    foreach (Rectangle r in graphicsSourceRects)
                    {
                        textures.Add(spritesheet.Crop(r));
                    }
                }
                isContentLoaded = true;
                isAnimated = is_animated;
                isRunning = true;
            }
        }

        public void UnloadContent()
        {
            textures.Clear();
        }

        public void SetData(int frame_length)
        {
            if (!isDataSet)
            {
                frameLength = frame_length;
                isDataSet = true;
            }
        }

        public void SetData(int frame_length, Vector2 Position)
        {
            if (!isDataSet)
            {
                frameLength = frame_length;
                position = Position;
                isDataSet = true;
            }
        }

        public void SetData(Vector2 Position)
        {
            if (!isDataSet)
            {
                position = Position;
                isDataSet = true;
            }
        }

        private void UpdateFrameCounter()
        {
            if (isAnimated)
            {
                if (framesDrawn == frameLength)
                {
                    if (frameIndex == textures.Count - 1)
                    {
                        if (isRunningOnce)
                        {
                            Stop();
                            isRunningOnce = false;
                            return;
                        }
                        frameIndex = 0;
                    }
                    else frameIndex++;
                    framesDrawn = 0;
                }
                framesDrawn++;
            }
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Vector2 Position)
        {
            if (isRunning)
            {
                UpdateFrameCounter();
                if (TransparencyMultiplier > 0.0f)
                {
                    spriteBatch.Draw(textures[frameIndex], Position, Color.White * TransparencyMultiplier);
                }
            }
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            if (isRunning)
            {
                UpdateFrameCounter();
                if (TransparencyMultiplier > 0.0f)
                {
                    spriteBatch.Draw(textures[frameIndex], position, Color.White * TransparencyMultiplier);
                }
            }
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, int frame)
        {
            Draw(gameTime, spriteBatch, frame, position);
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, int frame, Vector2 Position)
        {
            if (!(frame < 0 || frame > textures.Count - 1))
            {
                spriteBatch.Draw(textures[frame], Position, Color.White * TransparencyMultiplier);
            }
            else throw new ArgumentOutOfRangeException("frame", string.Format("The frame index {0} for drawing this tile is out of range.", frame.ToString()));
        }

        public WorldTile Clone()
        {
            WorldTile t = new WorldTile();
            t.textures = textures;
            t.graphicsSource = graphicsSource;
            t.graphicsSourceRects = graphicsSourceRects;
            t.isAnimated = isAnimated;
            t.transparencyMultiplier = transparencyMultiplier;
            t.position = position;
            return t;
        }

        public void Start()
        {
            isRunning = true;
            HasAnimationEnded = false;
        }

        public void Stop()
        {
            isRunning = false;
            HasAnimationEnded = true;
        }

        public void Reset()
        {
            frameIndex = 0;
            framesDrawn = 0;
        }

        public void PlayOnce()
        {
            // plays an animation once
            if (!isRunning)
            {
                isRunningOnce = true;
                Start();
            }
        }

        public void Overlay(ContentManager Content, string resourcePath, Rectangle sourceRect)
        {
            Texture2D overlay = Content.Load<Texture2D>(resourcePath).Crop(sourceRect);
            for (int i = 0; i < textures.Count - 1; i++)
            {
                textures[i] = GraphicsHelper.Overlay(textures[i], overlay);
            }
        }

        public void Overlay(GraphicsDevice Graphics, string resourcePath, Rectangle sourceRect)
        {
            Texture2D overlay = null;
            overlay = overlay.LoadFromFile(resourcePath, ref Graphics);
            for (int i = 0; i < textures.Count - 1; i++)
            {
                textures[i] = GraphicsHelper.Overlay(textures[i], overlay);
            }
        }
    }
}
